package services

import (
	"fmt"
	"gitee.com/zhoulvvv/my_go_gateway/dao"
	"gitee.com/zhoulvvv/my_go_gateway/public"
	"gitee.com/zhoulvvv/my_go_gateway/reverse_proxy/load_balance"
	"github.com/e421083458/golang_common/lib"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"
)

var (
	LoadBalancerHandler *LoadBalanceServices
	SLBSHandler         *ServiceLoadBalanceServices
)

type ServiceLoadBalanceServices struct {
	tx *gorm.DB
}

type LoadBalanceServices struct {
	// 每个服务对应一个负载均衡器
	LoadBalanceMap   map[string]*LoadBalanceItem
	LoadBalanceSlice []*LoadBalanceItem
	// 每个服务对应一个 http 的连接池
	TransportMap   map[string]*TransportItem
	TransportSlice []*TransportItem
	Locker         sync.RWMutex
}

type TransportItem struct {
	Trans       *http.Transport
	ServiceName string
}

type LoadBalanceItem struct {
	LoadBalance load_balance.LoadBalance
	ServiceName string
}

func init() {
	LoadBalancerHandler = NewLoadBalanceServices()
}

func NewLoadBalanceServices() *LoadBalanceServices {
	return &LoadBalanceServices{
		LoadBalanceMap:   map[string]*LoadBalanceItem{},
		LoadBalanceSlice: []*LoadBalanceItem{},
		TransportMap:     map[string]*TransportItem{},
		TransportSlice:   []*TransportItem{},
		Locker:           sync.RWMutex{},
	}
}

func NewServiceLoadBalanceServices() (*ServiceLoadBalanceServices, error) {
	if SLBSHandler != nil {
		return SLBSHandler, nil
	}
	tx, err := lib.GetGormPool("default")
	if err != nil {
		return nil, errors.New("获取数据库连接池失败")
	}
	return &ServiceLoadBalanceServices{
		tx: tx,
	}, nil
}

// 根据服务的负载均衡配置，获取服务的连接池
// TODO 优化点 5
func (lbs *LoadBalanceServices) GetTransport(c *gin.Context, service *dao.ServiceDetail) (*http.Transport, error) {
	for _, transItem := range lbs.TransportSlice {
		if transItem.ServiceName == service.Info.ServiceName {
			return transItem.Trans, nil
		}
	}
	if service.LoadBalance.UpstreamConnectTimeout == 0 {
		service.LoadBalance.UpstreamConnectTimeout = 30
	}
	if service.LoadBalance.UpstreamMaxIdle == 0 {
		service.LoadBalance.UpstreamMaxIdle = 100
	}
	if service.LoadBalance.UpstreamIdleTimeout == 0 {
		service.LoadBalance.UpstreamIdleTimeout = 90
	}
	if service.LoadBalance.UpstreamHeaderTimeout == 0 {
		service.LoadBalance.UpstreamHeaderTimeout = 30
	}
	trans := &http.Transport{
		DialContext: (&net.Dialer{
			Timeout:   time.Duration(service.LoadBalance.UpstreamConnectTimeout) * time.Second, //连接超时
			KeepAlive: 30 * time.Second,
		}).DialContext,
		ForceAttemptHTTP2:     true,
		TLSHandshakeTimeout:   10 * time.Second,
		MaxIdleConns:          service.LoadBalance.UpstreamMaxIdle,                                    //最大空闲连接
		IdleConnTimeout:       time.Duration(service.LoadBalance.UpstreamIdleTimeout) * time.Second,   //空闲超时时间
		ResponseHeaderTimeout: time.Duration(service.LoadBalance.UpstreamHeaderTimeout) * time.Second, //下游获取header超时时间
		ExpectContinueTimeout: 1 * time.Second,                                                        //100-continue超时时间
	}
	transItem := &TransportItem{Trans: trans, ServiceName: service.Info.ServiceName}
	lbs.TransportSlice = append(lbs.TransportSlice, transItem)
	lbs.Locker.Lock()
	defer lbs.Locker.Unlock()
	lbs.TransportMap[service.Info.ServiceName] = transItem
	return trans, nil
}

// 根据服务的负载均衡配置，获取负载均衡实例
func (lbs *LoadBalanceServices) GetLoadBalancer(c *gin.Context, service *dao.ServiceDetail) (load_balance.LoadBalance, error) {
	for _, loadBalanceItem := range lbs.LoadBalanceSlice {
		if loadBalanceItem.ServiceName == service.Info.ServiceName {
			return loadBalanceItem.LoadBalance, nil
		}
	}
	schema := "http://"
	if service.HTTPRule.NeedHttps == 1 {
		schema = "https://"
	}
	if service.Info.LoadType == public.LoadTypeTCP || service.Info.LoadType == public.LoadTypeGRPC {
		schema = ""
	}
	//prefix := ""
	//if service.HTTPRule.RuleType == public.HTTPRuleTypePrefix {
	//	prefix = service.HTTPRule.Rule
	//}
	ipList := SLBSHandler.GetIPListByModel(c, service.LoadBalance)
	weightList := SLBSHandler.GetWeightListByModel(c, service.LoadBalance)
	confMap := map[string]string{}
	for index, ipItem := range ipList {
		confMap[ipItem] = weightList[index]
	}
	mConf, err := load_balance.NewLoadBalanceCheckConf(fmt.Sprintf("%s%s", schema, "%s"),
		confMap)
	if err != nil {
		return nil, err
	}
	lb := load_balance.LoadBalanceFactorWithConf(load_balance.LbType(service.LoadBalance.RoundType), mConf)
	loadBalanceItem := &LoadBalanceItem{
		LoadBalance: lb,
		ServiceName: service.Info.ServiceName,
	}
	lbs.LoadBalanceSlice = append(lbs.LoadBalanceSlice, loadBalanceItem)
	lbs.Locker.Lock()
	defer lbs.Locker.Unlock()
	lbs.LoadBalanceMap[service.Info.ServiceName] = loadBalanceItem
	return lb, nil
}

// 获取IP列表
func (lbs *ServiceLoadBalanceServices) GetIPListByModel(c *gin.Context, loadBalanceModel *dao.LoadBalance) []string {
	return strings.Split(loadBalanceModel.IpList, ",")
}

// 获取权重列表
func (lbs *ServiceLoadBalanceServices) GetWeightListByModel(c *gin.Context, loadBalanceModel *dao.LoadBalance) []string {
	return strings.Split(loadBalanceModel.WeightList, ",")
}
